package furnitures 
{
	import alternativa.engine3d.core.Face;
	import alternativa.engine3d.core.Vertex;
	import alternativa.engine3d.materials.FillMaterial;
	import alternativa.engine3d.materials.Material;
	import alternativa.engine3d.materials.TextureMaterial;
	import alternativa.engine3d.objects.Mesh;
	import alternativa.engine3d.primitives.Box;
	import app.AppSettings;
	import debug.DebugView;
	import drawing.DrawTool;
	import drawing.IPalette;
	import factory.BitmapFactory;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Shader;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.geom.Bezier;
	import flash.geom.Matrix;
	import flash.geom.Matrix3D;
	import flash.geom.Orientation3D;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.geom.Vector3D;
	import furnitures.parts.FaceBox;
	import furnitures.parts.Handle;
	import gui.PositionInfo;
	import resources.Images;
	import utils.ArrayUtil;
	import utils.Logger;
	import utils3D.Converter;
	import utils3D.ShapeData;
	import utils3D.ShapeFactory;
	/**
	 * ...
	 * @author Dmitriy Gaiduk
	 */
	public class FurnitureMesh extends Mesh
	{
		public var furnName:String;
		
		private var _model:FurnitureModel;
		public var positionY:Number = 0;
		private var _edgePoints:Array;	
		private var _scale:Number;
		
		private var _debug:DebugView;
	//	public static const CORPUS_FACE:String = "CORPUS_FACE";
	//	public static const FACADE_FACE:String = "FACADE_FACE";
		private var innerMatrix:Matrix3D;
		private var _buildUp:Boolean;
		private var _buildBottom:Boolean;
		
		public function FurnitureMesh(model:FurnitureModel, scaleFactor:Number, 
							buildUp:Boolean, buildBottom:Boolean, nm:String = "", debugV:DebugView = null ) 
		{
			_buildUp = buildUp;
			_buildBottom = buildBottom;
			furnName = nm;
			_model = model.clone();
			_scale = scaleFactor;
			_debug = debugV;
			initFurniture();
		}
		
		private function initFurniture():void 
		{
			var v:Vector.<Vector3D> = new Vector.<Vector3D>();
			var position:Vector3D = new Vector3D(0, 0, 0);
			var angle:Vector3D = new Vector3D(0, 0, 0);
			var scale:Vector3D = new Vector3D(1, 1, 1);
			v.push(position, angle, scale);
			innerMatrix = new Matrix3D();
			innerMatrix.recompose(v, Orientation3D.EULER_ANGLES);
			
			//adjustDoorsLines();
			adjustScale();
			
			//adjustDoorsSide();
			switch(_model.shapePointsSide)
			{
				case FurnitureModel.SHAPE_SIDE_FRONT:
						adjustDoorsSide();
						angle.y =  180 * Math.PI / 180;
						angle.z =  180 * Math.PI / 180;
						innerMatrix.recompose(v, Orientation3D.EULER_ANGLES);
						buildFurnitureFromFront();
					break;
				case FurnitureModel.SHAPE_SIDE_TOP:
						angle.x =  -90 * Math.PI / 180;
						
						
						innerMatrix.recompose(v, Orientation3D.EULER_ANGLES);
						buildFurnitureFromTop();
					break;
				case FurnitureModel.SHAPE_SIDE_LEFT:
						angle.y =  90 * Math.PI / 180;
						angle.z =  90 * Math.PI / 180;
						innerMatrix.recompose(v, Orientation3D.EULER_ANGLES);
						adjustHandlePosition();
						buildFurnitureFromLeft();
						
					break;
				default:
						log("default for: " + _model.shapePointsSide);
						buildFurnitureFromTop();
					break;
			}
			
			// set position
			//log(furnName+ " : position " + _model.positionY);
			// on floor
			positionY = 0;
			if (_model.positionY == 0 )
			{
				positionY = 0;
			}else
			{
				// on wall
				positionY = _model.positionY - furnitureHeight;
			}
			
			if (_model.extra == FurnitureModel.EXTRA_GLASS ||
				_model.extra == FurnitureModel.EXTRA_GRID)
			{
				addExtraShape();
			}
		}
		
		private function addExtraShape():void 
		{
			
			var sideInfo:SideInfo;
			var count:int = faces.length;
			var facesForCheck:Vector.<Face> = new Vector.<Face>();
			for (var i:int = 0; i < count; i++) {
				facesForCheck.push(faces[i]);
			}
			
			while (facesForCheck.length > 0 ) {
				sideInfo = facesForCheck[0].id as SideInfo;
				if (sideInfo == null) {
					facesForCheck.splice(0, 1);
					continue;
				}
				
				if (sideInfo.type != SideInfo.FACADE_FACE) {
					facesForCheck.splice(0, 1);
					continue;
				}
				
				
				if (sideInfo.face.doors == null || sideInfo.face.doors.length == 0) {
					facesForCheck.splice(0, 1);
					continue;
				}
				
				// at this point it facade face => build shape
				var spacing:Number = FurnitureModel.EXTRA_SPACING * _scale;
				for (var j:int = 0 ; j <  sideInfo.face.doors.length; j++ ) {
					if (sideInfo.face.doors[j].doorShape != null) {
						sideInfo.face.doors[j].extraShape = ArrayUtil.cloneShapeArray(sideInfo.face.doors[j].doorShape);
						var rect:Rectangle = ArrayUtil.getShapeRect(sideInfo.face.doors[j].doorShape);
						var targetW:Number = rect.width - spacing * 2;
						var targetH:Number = rect.height - spacing * 2;
						var shapeScaleX:Number = targetW / rect.width;
						var shapeScaleY:Number = targetH / rect.height;
						
						ArrayUtil.scaleShapePoints(sideInfo.face.doors[j].extraShape, shapeScaleX, shapeScaleY);
						ArrayUtil.shiftShapeArray(sideInfo.face.doors[j].extraShape, spacing, spacing);
					}
				}
				
				// exclude faces whith this sideInfo
				facesForCheck.splice(0, 1);
				
				var faceForExclude:Vector.<Face> = new Vector.<Face>();
				count = facesForCheck.length;
				for (var k:int = 0 ; k < count; k++) {
					if (facesForCheck[k].id == sideInfo) {
						faceForExclude.push(facesForCheck[k]);
					}
				}
				
				var index:int;
				for (k = 0; k < faceForExclude.length; k++ ) {
					index = facesForCheck.indexOf(faceForExclude[k]);
					if (index < 0) {
						
						continue;
					}
					facesForCheck.splice(index, 1);
				}
			}
		}
		
		private function adjustHandlePosition():void 
		{
			if (_model.faces == null) {
				return;
			}
			
			var pos:PositionInfo;
			for (var i:int = 0 ; i < _model.faces.length; i++ ) {
				if (_model.faces[i].doors == null) {
					continue;
				}
				
				for (var j:int = 0; j < _model.faces[i].doors.length; j++ ) {
					if (_model.faces[i].doors[j].handles == null) {
						continue;
					}
					for (var k:int = 0 ; k < _model.faces[i].doors[j].handles.length; k++) {
						pos = _model.faces[i].doors[k].handles[k].posInfo;
						 _model.faces[i].doors[j].handles[k].posInfo = adjustPos(pos);
						 _model.faces[i].doors[j].handles[k].handleType = 
							reverseHandleType(_model.faces[i].doors[j].handles[k].handleType);
					}
					
				}
			}
		}
		
		private function reverseHandleType(type:int):int {
			if (type == Handle.HANDLE_HORIZONTAL) {
				return Handle.HANDLE_VERTICAL
			}else {
				return Handle.HANDLE_HORIZONTAL;
			}
		}
		
		private function adjustPos(pos:PositionInfo):PositionInfo 
		{
			var posReturn:PositionInfo = new PositionInfo();
			if ((pos.align & PositionInfo.ALIGN_LEFT) > 0) {
				posReturn.addAlign(PositionInfo.ALIGN_TOP);
			}
			
			if ((pos.align & PositionInfo.ALIGN_RIGHT) > 0) {
				posReturn.addAlign(PositionInfo.ALIGN_BOTTOM);
			}
			
			if ((pos.align & PositionInfo.ALIGN_BOTTOM) > 0) {
				posReturn.addAlign(PositionInfo.ALIGN_LEFT);
			}
			
			if ((pos.align & PositionInfo.ALIGN_TOP) > 0) {
				posReturn.addAlign(PositionInfo.ALIGN_RIGHT);
			}
			
			if ((pos.align & PositionInfo.ALIGN_CENTR_X) > 0) {
				posReturn.addAlign(PositionInfo.ALIGN_CENTR_Y);
			}
			
			if ((pos.align & PositionInfo.ALIGN_CENTR_Y) > 0) {
				posReturn.addAlign(PositionInfo.ALIGN_CENTR_X);
			}
			
			posReturn.posY = pos.posX;
			posReturn.topPadding = pos.leftPadding;
			posReturn.bottomPadding = pos.rightPadding;
			posReturn.leftPadding = pos.bottomPadding;
			posReturn.rightPadding = pos.topPadding;
			
			return posReturn;
		}
		
		private function buildFurnitureFromLeft():void 
		{
			createSurfacesFromTop(_model, _model.furnitureHeight, 
										_model.furnitureWidth,
										_model.furnitureDepth);
			
			// setup faces
			var sideInfo:SideInfo;
			for (var i:int = 0 ; i < faces.length; i++ )
			{
				sideInfo = faces[i].id as SideInfo;
				if (sideInfo != null)
				{
					sideInfo.face = getFaceAtIdex(sideInfo.index);
				}
				
				if (sideInfo.face != null)
				{
					
					for each(var door:FaceBox in sideInfo.face.doors)
					{
						
						if (door.doorShape == null)
						{
							var	doorW:Number = 
								sideInfo.width * door.posRD.x - sideInfo.width * door.positionUV.x;
							var	doorH:Number = 
							sideInfo.height * door.posRD.y - sideInfo.height * door.positionUV.y;
							
							var startX:Number = sideInfo.width * door.positionUV.x;
							var startY:Number = sideInfo.height * door.positionUV.y;
							door.doorShape = makeRectangleShape(doorW, doorH, 0, 0);
						}
					}
				}
			}
		}
		
	
		private function buildFurnitureFromFront():void 
		{
			createSurfacesFromTop(_model, _model.furnitureWidth, 
										_model.furnitureDepth,
										_model.furnitureHeight);
			// setup faces
			var sideInfo:SideInfo;
			for (var i:int = 0 ; i < faces.length; i++ )
			{
				sideInfo = faces[i].id as SideInfo;
				if (sideInfo != null)
				{
					sideInfo.face = getFaceAtIdex(sideInfo.index);
					if (sideInfo.index == -1)
					{
						if(_model.faces!=null && _model.faces.length>0){
							sideInfo.face = _model.faces[0];
							
							if(sideInfo.face.doors!=null && sideInfo.face.doors.length >0){
								sideInfo.face.doors[0].doorShape = sideInfo.textureShape;
							}
						}
					}
				}
				
				if (sideInfo.face != null)
				{
					for each(var door:FaceBox in sideInfo.face.doors)
					{
						if (door.doorShape == null)
						{
							var	doorW:Number = 
								sideInfo.width * door.posRD.x - sideInfo.width * door.positionUV.x;
							var	doorH:Number = 
							sideInfo.height * door.posRD.y - sideInfo.height * door.positionUV.y;
							
							var startX:Number = sideInfo.width * door.positionUV.x;
							var startY:Number = sideInfo.height * door.positionUV.y;
							door.doorShape = makeRectangleShape(doorW, doorH, 0, 0);
						}
					}
				}
			}
		}
		
		private function adjustScale():void 
		{
			// furnModel.faceAnchorCurve 
			
			_model.furnitureWidth *= _scale;
			_model.furnitureHeight *= _scale;
			_model.furnitureDepth *= _scale;
			_model.positionY *= _scale;
			
			var pointsXY:Array;
			var i:int;
			if(_model.shapePoints!=null){
				scaleArrays(_model.shapePoints, _scale);
				/*for (i =0 ; i < _model.shapePoints.length; i++)
				{
					pointsXY = _model.shapePoints[i];
					for (var j:int = 0; j < pointsXY.length; j++)
					{
						pointsXY[j] *= _scale;
					}
				}*/
			}
			
			if (_model.faceAnchorCurve != null)
			{
				_model.faceAnchorCurve.y *= _scale;
			}
			
			// foots
			if (_model.foots != null) {
				for (i = 0 ; i < _model.foots.positions.length; i++ ) {
					 _model.foots.positions[i].x *= _scale;
					 _model.foots.positions[i].y *= _scale;
				}
			}
			
			//handles
			if (_model.faces != null) {
				for (i = 0; i < _model.faces.length; i++ ) {
					 if (_model.faces[i].doors == null) {
						 continue;
					 }
					for (var j:int = 0 ; j <  _model.faces[i].doors.length; j++ ) {
						scaleArrays(_model.faces[i].doors[j].doorShape, _scale);
						scaleArrays(_model.faces[i].doors[j].lines, _scale);
						// handles
						if(_model.faces[i].doors[j].handles!=null){
							for (var k:int = 0; k <  _model.faces[i].doors[j].handles.length; k++ ) {
								scalePositionInfo(_model.faces[i].doors[j].handles[k].posInfo);
							}
						}
					}
				}
			}
			
		}
		
		
		
		private function scalePositionInfo(pos:PositionInfo):void {
			pos.posX *= _scale;
			pos.posY *= _scale;
			pos.bottomPadding  *= _scale;
			pos.topPadding  *= _scale;
			pos.leftPadding  *= _scale;
			pos.rightPadding  *= _scale;
		}
		
		private function scaleArrayXY(arr:Array, factor:Number):void {
			if (arr == null ) {
				return;
			}
			for (var i:int = 0; i < arr.length; i++)
			{
				arr[i] *= factor;
			}
		}
		
		private function scaleArrays(arr:Array, factor:Number):void {
			if (arr == null ) {
				return;
			}
			for (var i:int =0 ; i < arr.length; i++)
			{
				scaleArrayXY(arr[i], factor);
			}
		}
		
		private function getFaceAtIdex(index:int):FurnitureFace
		{
			for each(var face:FurnitureFace in _model.faces)
			{
				if (face.index == index)
				{
					return face.clone();
				}
			}
			return null;
		}
		
		private function buildFurnitureFromTop():void 
		{
			createSurfacesFromTop(_model, _model.furnitureWidth, 
			_model.furnitureHeight, _model.furnitureDepth);
			
			// setup faces
			var sideInfo:SideInfo;
			for (var i:int = 0 ; i < faces.length; i++ )
			{
				sideInfo = faces[i].id as SideInfo;
				if (sideInfo != null)
				{
					sideInfo.face = getFaceAtIdex(sideInfo.index);
				}
				
				if (sideInfo.face != null)
				{
					for each(var door:FaceBox in sideInfo.face.doors)
					{
						
						if (door.doorShape == null)
						{
							var	doorW:Number = 
								sideInfo.width * door.posRD.x - sideInfo.width * door.positionUV.x;
							var	doorH:Number = 
							sideInfo.height * door.posRD.y - sideInfo.height * door.positionUV.y;
							
							var startX:Number = sideInfo.width * door.positionUV.x;
							var startY:Number = sideInfo.height * door.positionUV.y;
							door.doorShape = makeRectangleShape(doorW, doorH, 0, 0);
						}
					}
				}
			}
		}
		
		private function adjustDoorsSide():void 
		{
			if (_model.side == FurnitureModel.SIDE_RIGHT) {
				return;
			}
		 
			for each (var face:FurnitureFace in _model.faces)
			{
				if (face.doors != null)
				{
					for each(var door:FaceBox in face.doors)
					{
						for each(var handle:Handle in door.handles) {
							
								handle.posInfo.reverseX();
						}
					}
				}
			}
			
		}
		
		private function createSurfacesFromTop(furnModel:FurnitureModel, 
								meshW:Number, meshH:Number, meshD:Number):void
		{
			if (furnModel.shapePoints != null && furnModel.shapePoints.length > 0)
			{
				//_edgePoints = makeEdgeSideAlign(furnModel.shapePoints);
				_edgePoints = furnModel.shapePoints;
			}else if (furnModel.faceAnchorCurve != null)
			{
				_edgePoints = makeCurveShape(furnModel);
			}else
			{
				_edgePoints = makeRectangleShape(meshW, furnModel.furnitureDepth);
			}
			
			_edgePoints = makeEdgeSideAlign(_edgePoints);
		
			var points:Vector.<Number>;
			var pointsXY:Array;
			var prevXY:Array;
			var shapeData:ShapeData;
			
			// start i=1 - build line with preview point
			prevXY = _edgePoints[0];
			
			
			var pointsXYZ:Vector.<Number>;
			var uvs:Vector.<Number> ;
			var mat:FillMaterial = new FillMaterial(0xFF0000);
			mat.lineColor = 0x000000;
			mat.lineThickness = 2;
			
			var matGreen:FillMaterial = new FillMaterial(0x00FF00);
			matGreen.lineColor = 0x000000;
			matGreen.lineThickness = 2;
			
			var invertIndeces:Vector.<int>;
			var vertices:Vector.<Vertex>;
			var material:Material;
			var sideInfo:SideInfo;
			var shapeWidth:Number
			if (furnName == "decor") {
				log("side: " + _model.side);
			}
			for (var i:int = 1; i < _edgePoints.length; i++ )
			{
				
				pointsXY = _edgePoints[i];
				
				if (pointsXY.length == 2)
				{
					points = new Vector.<Number>();
					points.push(prevXY[0],prevXY[1],pointsXY[0],pointsXY[1]);
					shapeData = ShapeFactory.getPlaneDataXY(points, meshH);
					
					// rotate 
					rotatePoints(shapeData.points);
					
					pointsXYZ = Converter.convertPoints(shapeData.points);
					uvs = Converter.convertUVT(shapeData.uvt, shapeData.indices);
					
					
					//addFace(makeVertices(pointsXYZ, uvs, shapeData.indices), mat, CORPUS_FACE);
					//addFace(makeVertices(pointsXYZ, uvs, invertIndeces), mat, CORPUS_FACE);
					//addVerticesAndFaces(pointsXYZ, uvs, shapeData.indices, false, mat);
					//addVerticesAndFaces(pointsXYZ, uvs, invertIndeces, false, mat);
					
					sideInfo = getFaceId(i);
					shapeWidth = calcLength(prevXY[0],prevXY[1],pointsXY[0],pointsXY[1]);
					sideInfo.textureShape = makeRectangleShape(shapeWidth, furnModel.furnitureHeight);
					sideInfo.width = shapeWidth;
					sideInfo.height = furnModel.furnitureHeight;
					
					material = mat;
					if (sideInfo.type == SideInfo.FACADE_FACE) {
						material = matGreen;
					}
				
					
					
					if(_model.side == FurnitureModel.SIDE_RIGHT){
						invertIndeces = invertArray(shapeData.indices);
						addFaces(pointsXYZ,  shapeData.uvt, invertIndeces,  material, sideInfo);
					}else {
						addFaces(pointsXYZ, shapeData.uvt, shapeData.indices, material, sideInfo);
					}
					
					if (_model.doubleSided) {
						if(_model.side == FurnitureModel.SIDE_RIGHT){
							addFaces(pointsXYZ, shapeData.uvt, shapeData.indices, material, sideInfo);
						}else {
							invertIndeces = invertArray(shapeData.indices);
							addFaces(pointsXYZ,  shapeData.uvt, invertIndeces,  material, sideInfo);
						}
					}
					
					prevXY = _edgePoints[i];
					
				}else if (pointsXY.length > 2)
				{
					// add curves
					points = new Vector.<Number>();
					var k:int = 0;
					// make points arr : 3 pair points
					var startXY:Array = [prevXY[0], prevXY[1]];
					while (k < pointsXY.length)
					{
						// pair points {x,y} start, control, end
						points.push(prevXY[0], prevXY[1], pointsXY[k+2], pointsXY[k+3], pointsXY[k], pointsXY[k+1]);
						prevXY = [pointsXY[k],  pointsXY[k + 1]];
						k += 4;
					}
					k -= 4; // setup current k
					
					sideInfo = getFaceId(i);
					shapeWidth = calcLength(startXY[0], startXY[1], pointsXY[k], pointsXY[k+1]);
					sideInfo.textureShape = makeRectangleShape(shapeWidth, furnModel.furnitureHeight);
					sideInfo.width = shapeWidth;
					sideInfo.height = furnModel.furnitureHeight;
					
					// make data
					shapeData = ShapeFactory.getBezierDataMulti(points, meshH);
					
					//rotate
					rotatePoints(shapeData.points);
					
					pointsXYZ = Converter.convertPoints(shapeData.points);
					uvs = Converter.convertUVT(shapeData.uvt, shapeData.indices);
					//uvs = new Vector.<Number>();
					
					
					
					//addVerticesAndFaces(pointsXYZ, uvs, shapeData.indices, false, matGreen);
					//addVerticesAndFaces(pointsXYZ, uvs, invertIndeces, false, matGreen);
					material = mat;
					if (sideInfo.type == SideInfo.FACADE_FACE) {
						material = matGreen;
					}
					
					if(_model.side == FurnitureModel.SIDE_RIGHT){
						invertIndeces = invertArray(shapeData.indices);
						addFaces(pointsXYZ, shapeData.uvt, invertIndeces, matGreen, sideInfo);
					}else {
						addFaces(pointsXYZ, shapeData.uvt, shapeData.indices, material, sideInfo);
					}
					
					if (_model.doubleSided) {
						if(_model.side == FurnitureModel.SIDE_RIGHT){
							addFaces(pointsXYZ, shapeData.uvt, shapeData.indices, material, sideInfo);
						}else {
							invertIndeces = invertArray(shapeData.indices);
							addFaces(pointsXYZ, shapeData.uvt, invertIndeces, matGreen, sideInfo);
						}
					}
					
					//addFace(makeVertices(pointsXYZ, uvs, invertIndeces), matGreen, CORPUS_FACE);
					//addFace(makeVertices(pointsXYZ, uvs, invertIndeces), matGreen, CORPUS_FACE);
					
				}else
				{
					throw Error("Wrong shape data");
				}
				
			}
			
			if (furnName == "decor") {
				calculateFacesNormals();
				return;
			}
			var upBitmap:BitmapData = BitmapFactory.createShapeColor(_edgePoints, 0x00FF00, 1);
			
			var frontSideInfo:SideInfo;
			var backSideInfo:SideInfo;
			var tempSideInfo:SideInfo;
			
			// up
			
			if (_buildUp) {
				frontSideInfo = getFaceId( -1);
				backSideInfo = _model.doubleSided ? getFaceId( -1) : null;
				
				frontSideInfo.textureShape = copyPoints(_edgePoints);
				frontSideInfo.width = upBitmap.width;
				frontSideInfo.height = upBitmap.height;
				
				if (backSideInfo != null) {
					backSideInfo.textureShape = copyPoints(_edgePoints);
					backSideInfo.width = upBitmap.width;
					backSideInfo.height = upBitmap.height;
				}
				
				// swap sides, if left
				if (_model.side == FurnitureModel.SIDE_LEFT) {
					tempSideInfo = frontSideInfo;
					frontSideInfo = backSideInfo;
					backSideInfo = tempSideInfo;
				}
				makeShape3D(_edgePoints, 0, frontSideInfo, backSideInfo);
			}
			
			// bottom
			if (_buildBottom) {
				frontSideInfo = _model.doubleSided ? getFaceId( -1) : null;
				if (frontSideInfo != null) {
					frontSideInfo.textureShape = copyPoints(_edgePoints);
					frontSideInfo.width = upBitmap.width;
					frontSideInfo.height = upBitmap.height;
				}
				
				backSideInfo = getFaceId( -2);
				backSideInfo.textureShape = copyPoints(_edgePoints);
				backSideInfo.width = upBitmap.width;
				backSideInfo.height = upBitmap.height;
				
				// swap sides, if left
				if (_model.side == FurnitureModel.SIDE_LEFT) {
					tempSideInfo = frontSideInfo;
					frontSideInfo = backSideInfo;
					backSideInfo = tempSideInfo;
				}
				
				makeShape3D(_edgePoints, meshH, frontSideInfo, backSideInfo);
			}
			
			calculateFacesNormals();
		}
		
		private function copyPoints(fromArr:Array):Array
		{
			var shapePoints:Array = [];
			var pointsXY:Array;
			var pointsXYClone:Array;
			for (var i:int = 0; i < fromArr.length; i++)
			{
				pointsXY = fromArr[i];
				pointsXYClone = [];
				for (var j:int = 0; j < pointsXY.length; j++)
				{
					pointsXYClone.push(pointsXY[j]);
				}
				shapePoints.push(pointsXYClone);
			}
			return shapePoints;
		}
		
		private function calcLength(x1:Number, y1:Number, x2:Number, y2:Number):Number {
			return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
		}
		
		private function rotatePoints(points:Vector.<Vector3D>):void {
			
			for (var i:int = 0 ; i < points.length; i++) {
				points[i] = rotatePoint3D(points[i]);
				//points[i].z += _model.furnitureHeight;
			}
			
			
			
		}
		
		private function rotatePoint3D(point:Vector3D):Vector3D {
			if (innerMatrix == null) {
				log(furnName +  ": inner matr null");
				return new Vector3D();
				
			}
			var p:Vector3D = innerMatrix.transformVector(point);
			
			if (_model.shapePointsSide == FurnitureModel.SHAPE_SIDE_TOP) {
				p.z += _model.furnitureHeight;
			}else  if(_model.shapePointsSide == FurnitureModel.SHAPE_SIDE_FRONT) {
				p.z += _model.furnitureHeight;
				p.y += _model.furnitureDepth;
			}else if(_model.shapePointsSide == FurnitureModel.SHAPE_SIDE_LEFT){
				p.x += _model.furnitureWidth;
				p.z += _model.furnitureHeight;
			}
			//p.x += _model.furnitureWidth;
			return p;
		}
		
		private function makeVertices(points:Vector.<Number>, 
							uvs:Vector.<Number>, indeces:Vector.<int>):Vector.<Vertex> {
			var vertices:Vector.<Vertex> = new Vector.<Vertex>();
			
			var vert:Vertex;
			for (var i:int = 0 ; i < indeces.length; i++) {
				vert = addVertex(points[indeces[i] * 3], points[indeces[i] * 3 + 1], points[indeces[i] * 3 + 2],
								uvs[indeces[i]], uvs[indeces[i] + 1]);
				vertices.push(vert);
			}
			return vertices;
			
		}
		
		private function addFaces(points:Vector.<Number>, 
							uvs:Vector.<Number>, indeces:Vector.<int>, mat:Material, idFace:Object):void{
			//var vertices:Vector.<Vertex> = new Vector.<Vertex>();
			
			//var vert:Vertex;
			var i:int = 0;
			var uvFactor:Number = 3;
			while (i < indeces.length) {
				var va:Vertex = addVertex(points[indeces[i] * 3], points[indeces[i] * 3 + 1], points[indeces[i] * 3 + 2],
								uvs[indeces[i]*uvFactor], uvs[indeces[i]*uvFactor + 1]);
				i++;
				var vb:Vertex = addVertex(points[indeces[i] * 3], points[indeces[i] * 3 + 1], points[indeces[i] * 3 + 2],
								uvs[indeces[i]*uvFactor], uvs[indeces[i]*uvFactor + 1]);
				i++;
				var vc:Vertex = addVertex(points[indeces[i] * 3], points[indeces[i] * 3 + 1], points[indeces[i] * 3 + 2],
								uvs[indeces[i]*uvFactor], uvs[indeces[i]*uvFactor + 1]);
				
				addTriFace(va, vb, vc, mat, idFace);
				//vert = addVertex(points[indeces[i] * 3], points[indeces[i] * 3 + 1], points[indeces[i] * 3 + 2],
								//uvs[indeces[i]*uvFactor], uvs[indeces[i]*uvFactor + 1]);
				//vertices.push(vert);
				
				i++;
			}
			
		}
		
		private function getFaceId(index:int):SideInfo {
			var sideInfo:SideInfo = new SideInfo();
			sideInfo.index = index;
			sideInfo.type = SideInfo.CORPUS_FACE;
			var i:int;
			
			switch(_model.shapePointsSide) {
				case FurnitureModel.SHAPE_SIDE_TOP:
						if (index == -1 && _model.hasTabletop) {
							sideInfo.type = SideInfo.TABLETOP_FACE;
						}
						if (_model.faces != null) {
							for (i = 0 ; i < _model.faces.length; i++ ) {
									if (_model.faces[i].index == index) {
										sideInfo.type = SideInfo.FACADE_FACE;
										break;
									}
								}
						}
					break;
				case FurnitureModel.SHAPE_SIDE_FRONT:
						if (index == -1) {
							sideInfo.type = SideInfo.FACADE_FACE;
							
						}
					break;
				case FurnitureModel.SHAPE_SIDE_LEFT:
						if (_model.faces != null) {
							for (i = 0 ; i < _model.faces.length; i++ ) {
									if (_model.faces[i].index == index) {
										sideInfo.type = SideInfo.FACADE_FACE;
										break;
									}
								}
						}
					break;
				case FurnitureModel.SHAPE_SIDE_LEFT:
					break;
			}
			
			return sideInfo;
		}
		
		private function debugPoint(xPos:Number, yPos:Number):void {
			
			if (_debug != null) {
				//_debug.addPoint(xPos, yPos);
			}
		}
		
		private function makeShape3D(pointsArr:Array, zPos:Number, frontName:Object, backName:Object):void {
			
			//var mat:FillMaterial = new FillMaterial(0xFF8000, 1, 1, 0x0080FF);
			var mat:FillMaterial = new FillMaterial(0xFF8000, 1, 1, 0x0080FF);
			var vertices:Vector.<Vertex> = new Vector.<Vertex>();
			var prevXY:Array = pointsArr[0];
			var pointsXY:Array;
			var counter:int = 0;
			var points: Vector.<Number>;
			var points3D: Vector.<Vector3D>;
			var point3D: Vector3D;
			var segmentCountPerLineW:int = 8; // segments in X axis for one curve
			var vert:Vertex;
			
			// first point
			point3D = new Vector3D(prevXY[0], zPos, prevXY[1]);
			point3D = rotatePoint3D(point3D);
			counter = vertices.length;
			vert = addVertex(point3D.x, point3D.y, point3D.z, getU(prevXY[0]), getV(prevXY[1]),
							"v" + counter.toString());
			vertices.push(vert);
			
			for (var i:int = 1; i < pointsArr.length; i++ )
			{
				pointsXY = pointsArr[i];
				
				if (pointsXY.length == 2)
				{
					
					point3D = new Vector3D(pointsXY[0], zPos, pointsXY[1]);
					point3D = rotatePoint3D(point3D);
					// next point
					counter = vertices.length;
					vert =  addVertex(point3D.x, point3D.y, point3D.z, 
											getU(pointsXY[0]), getV(pointsXY[1]),
										"v" + counter.toString());
					debugPoint(pointsXY[0], pointsXY[1]);
					vertices.push(vert);
					prevXY = pointsArr[i];
					
				}else if (pointsXY.length > 2) {
					// add curves
					points = new Vector.<Number>();
					var k:int = 0;
					// make points arr : 3 pair points
					var startXY:Array = [prevXY[0], prevXY[1]];
					while (k < pointsXY.length)
					{
						// pair points {x,y} start, control, end
						points.push(prevXY[0], prevXY[1], pointsXY[k+2], pointsXY[k+3], pointsXY[k], pointsXY[k+1]);
						prevXY = [pointsXY[k],  pointsXY[k + 1]];
						k += 4;
					}
					
					
					// make curve
					k = 0;
					while (k < points.length)
					{
						// начальная точка, она же конечная точка предыдущей кривой для количества кривых >1
						var start:Point = new Point(points[k], points[k + 1]); 
						// точка управления, куда стремиться искривление
						var control:Point = new Point(points[k + 2], points[k + 3]); 
						// конечная
						var end:Point = new Point(points[k+4], points[k+5]); 
						
						var bezier:Bezier = new Bezier(start, control, end); // строим модель кривой, без представления
						// height of segment
						var bezierT:Number; //0..1
						var bezierStep:Number = 1 / segmentCountPerLineW;
						//define bezier points
						var bezierPoint:Point;
						var ti:int = k == 0 ? 0 :1;
						
						for (ti; ti <= segmentCountPerLineW; ti++)
						{
							bezierT = bezierStep * ti;
							if (ti == segmentCountPerLineW)
							{
								bezierT = 1;
							}
							bezierPoint = bezier.getPoint(bezierT);
							
							// add curve
							// add vertex
							//rotate
							point3D = new Vector3D(bezierPoint.x, zPos, bezierPoint.y);
							point3D = rotatePoint3D(point3D);
					
							counter = vertices.length;
							vert = addVertex(point3D.x, point3D.y, point3D.z,
												getU(bezierPoint.x), getV(bezierPoint.y),
											"v" + counter.toString());
							debugPoint(bezierPoint.x, bezierPoint.y);
							vertices.push(vert);
						}
						
						
						counter++;
						k += 6; // 3 x pair point {x,y}
					}
				}
			}
			
			if(frontName!=null){
				addFace(vertices, mat, frontName);
			}
			if(backName!=null){
				vertices.reverse();
				mat = new FillMaterial(0xBB5E00, 1, 1, 0xFF80FF);
				addFace(vertices, mat, backName);
			}
			//return new ShapeData();
		}
		
		
		
		private function getV(yPos:Number):Number {
		//	log("v: " + (furnitureDepth / yPos));
			if(_model.shapePointsSide == FurnitureModel.SHAPE_SIDE_TOP){
				return yPos / furnitureDepth;
			}else if(_model.shapePointsSide == FurnitureModel.SHAPE_SIDE_FRONT){
				return yPos / furnitureHeight;
			}
			
			return yPos / furnitureDepth;
		}
		
		private function getU(xPos:Number):Number {
			if (_model.shapePointsSide == FurnitureModel.SHAPE_SIDE_LEFT) {
				return xPos / furnitureHeight;
			}
			return xPos / furnitureWidth;
		}
		
		
		private function invertArray(arr:Vector.<int>):Vector.<int> {
			
			var invert:Vector.<int> = new Vector.<int>();
			for (var i:int = 0 ; i < arr.length; i++ ) {
				invert.push(arr[arr.length - i - 1]);
			}
			return invert;
			
		}
		
		private function makeEdgeSideAlign(edge:Array):Array
		{
			if (_model.side == FurnitureModel.SIDE_RIGHT)
			{
				return edge;
			}else if (_model.side != FurnitureModel.SIDE_LEFT)
			{
				return edge;
			}
			
			var maxX:Number = 0;
			var minX:Number = Number.MAX_VALUE;
			var points:Array = [];
			var pointsXY:Array;
			var pointsXYAdjusted:Array;
			var i:int;
			var j:int;
			for (i = 0; i < edge.length; i++ )
			{
				pointsXY = edge[i];
				for (j = 0; j < pointsXY.length; j += 2 )
				{
					maxX = Math.max(maxX, pointsXY[j]);
					minX = Math.min(minX, pointsXY[j]);
				}
			}
			
			var shapeWidth:Number = maxX - minX;
			
			for (i = 0; i < edge.length; i++ )
			{
				pointsXY = edge[i];
				pointsXYAdjusted = [];
				
				for (j = 0; j < pointsXY.length; j += 2 )
				{
					pointsXYAdjusted.push(maxX - pointsXY[j] + minX);
					pointsXYAdjusted.push(pointsXY[j+1]);
				}
				
				points.push(pointsXYAdjusted);
			}
			
			return points;
		}
		
		private function makeCurveShape(model:FurnitureModel):Array
		{
			var points:Array = [];
			points.push([0, 0]);
			points.push([model.furnitureWidth, 0]);
			points.push([model.furnitureWidth, model.furnitureDepth]);
			points.push([0, model.furnitureDepth, 
						 model.faceAnchorCurve.x * model.furnitureWidth, 
						 model.furnitureDepth + model.faceAnchorCurve.y]);
			points.push([0, 0]);
			return points;
		}
		
		
		
		
		private function makeRectangleShape(rectWidth:Number, rectHeight:Number, 
											startX:Number = 0,
											startY:Number = 0 ):Array
		{
			var points:Array = [];
			points.push([startX, startY]);
			points.push([startX+ rectWidth, startY]);
			points.push([startX + rectWidth, startY + rectHeight]);
			points.push([startX, startY + rectHeight]);
			points.push([startX, startY]);
			return points;
		}
		
		public function get furnitureHeight():Number 
		{
			return _model.furnitureHeight;
		}
		
		public function get furnitureWidth():Number 
		{
			return _model.furnitureWidth;
		}
		
			public function get furnitureDepth():Number 
		{
			return _model.furnitureDepth;
		}
		
		public function get model():FurnitureModel 
		{
			return _model;
		}
		
		public function get edgePoints():Array 
		{
			return _edgePoints;
		}
		
		private function log(msg:String):void
		{
			Logger.log(this, msg);
		}
		
		
		public function setMaterial(faceName:String, mat:Material):void {
			for (var i:int = 0 ; i < faces.length; i++ ) {
				if (faces[i].id == faceName) {
					faces[i].material = mat;
				}
			}
			
		}
		
		public function setFacadePalette(facadePalette:IPalette, techPalette:IPalette):void {
			
			
			var shape:Sprite = new Sprite();
			var gr:Graphics = shape.graphics;
			var bm:BitmapData;
			var sideInfo:SideInfo;
			
			var count:int = faces.length;
			var facesForCheck:Vector.<Face> = new Vector.<Face>();
			for (var i:int = 0; i < count; i++) {
				facesForCheck.push(faces[i]);
			}
			
			// обход всех поверхностей
			while(facesForCheck.length > 0){
				
				sideInfo = facesForCheck[0].id as SideInfo;
				
				// нужны только поверхности для фасада
				if (sideInfo == null) {
					log("Side info null: " + faces[i].id);
					facesForCheck.splice(0, 1);
					continue;
				}
				
				if (sideInfo.type != SideInfo.FACADE_FACE) {
					facesForCheck.splice(0, 1);
					continue;
				}
				
			
				var boxFace:FurnitureFace = sideInfo.face;
					// draw face
					gr.clear();
					//draw doors
					var doorW:Number;
					var doorH:Number;
					if (boxFace == null)
					{
						log("Draw face null");
						continue;
					}
					var reverseByX:Boolean = _model.side == FurnitureModel.SIDE_RIGHT? true : false;
					
					for each(var door:FaceBox in boxFace.doors)
					{
						if (_model.extra == FurnitureModel.EXTRA_NO_FACE) {
							continue;
						}
						
						var doorShape:Shape = new Shape();
						doorW = sideInfo.width * door.posRD.x - sideInfo.width * door.positionUV.x;
						doorH = sideInfo.height * door.posRD.y - sideInfo.height * door.positionUV.y;
						var startX:Number = sideInfo.width * door.positionUV.x;
						var startY:Number = sideInfo.height * door.positionUV.y;
						
						if (door.type == FaceBox.TYPE_TECH && techPalette!=null) {
							techPalette.beginFill(doorShape.graphics, _model.getDoorSize(door), doorW, doorH,
												reverseByX);
						}else if(facadePalette!=null){
							
							facadePalette.beginFill(doorShape.graphics, _model.getDoorSize(door), doorW, doorH,
											reverseByX);
						}
						/*gr.drawRect(sideInfo.width * door.positionUV.x, 
									sideInfo.height * door.positionUV.y, 
									doorW, doorH);*/
						DrawTool.drawEdge(doorShape.graphics, door.doorShape);
						
						if (door.type == FaceBox.TYPE_TECH && techPalette!=null) {
							techPalette.endFill(doorShape.graphics);
						}else if(facadePalette != null){
							facadePalette.endFill(doorShape.graphics);
						}
						
						doorShape.x = startX;
						doorShape.y = startY;
						// draw extra - glass/grid
						if (door.allowExtra &&
								(_model.extra == FurnitureModel.EXTRA_GLASS || 
								_model.extra == FurnitureModel.EXTRA_GRID)) {
							if (door.extraShape != null) {
								if (facadePalette.beginFill(doorShape.graphics, _model.extra, doorW, doorH)) {
									DrawTool.drawEdge(doorShape.graphics, door.extraShape);
									facadePalette.endFill(doorShape.graphics);
								}
								
							}
						}
						// draw lines
						if (door.lines != null)
						{
							if (facadePalette!= null && facadePalette.lineStyle(gr))
							{
							//doorShape.graphics.lineStyle(10, 0xFF0000);
								DrawTool.drawLines(doorShape.graphics, door.lines);
							}
						}
						
						
						if (door.handles != null)
						{
							for each(var handle:Handle in door.handles)
							{
								var handleName:String = handle.handleType == Handle.HANDLE_VERTICAL ?
														Images.HANDLE_VERT : Images.HANDLE_HORIZ;
								
								var bmData:BitmapData = Images.getImageBitmap(handleName);
								if (bmData != null)
								{
									//log("Draw handle: " + handleName);
									var matr:Matrix = new Matrix();
									matr.tx = handle.posInfo.getPosX(doorW, bmData.width);
									matr.ty = handle.posInfo.getPosY(doorH, bmData.height);
									doorShape.graphics.lineStyle();
									doorShape.graphics.beginBitmapFill(bmData, matr, false, true);
									doorShape.graphics.drawRect(matr.tx, matr.ty, bmData.width, bmData.height);
									doorShape.graphics.endFill();
								}
							}
						}
						shape.addChild(doorShape);
					}
					
					var shapeHolder:Sprite = new Sprite();
					shapeHolder.addChild(shape);
					// reverse by Y axis
					//shape.scaleX = -1;
					//shape.x = sideInfo.width; //  excluding line width - not shape.height!!
					bm = new BitmapData(sideInfo.width, sideInfo.height, true, 0xFF0000);
					bm.draw(shapeHolder, null, null, null, null, true);
					
					
					// setup material for faces whith this sideInfo
					var textureMat:TextureMaterial = new TextureMaterial(bm, false, true, 0, 1);
					facesForCheck[0].material = textureMat;
					facesForCheck.splice(0, 1);
					
					var faceForExclude:Vector.<Face> = new Vector.<Face>();
					count = facesForCheck.length;
					
					for (var k:int = 0 ; k < count; k++) {
						if (facesForCheck[k].id == sideInfo) {
							facesForCheck[k].material = textureMat;
							faceForExclude.push(facesForCheck[k]);
						}
					}
					
					var index:int;
					for (k = 0; k < faceForExclude.length; k++ ) {
						index = facesForCheck.indexOf(faceForExclude[k]);
						if (index < 0) {
							
							continue;
						}
						facesForCheck.splice(index, 1);
					}
					
				}
			
		}
		
		public function setTabletopPalette(palette:IPalette):void
		{
			if (palette == null)
			{
				return;
			}
			
			var shape:Shape = new Shape();
			var gr:Graphics = shape.graphics;
			var bm:BitmapData;
			var sideInfo:SideInfo;
			
			var count:int = faces.length;
			var facesForCheck:Vector.<Face> = new Vector.<Face>();
			for (var i:int = 0; i < count; i++) {
				facesForCheck.push(faces[i]);
			}
			
			// обход всех поверхностей
			while (facesForCheck.length > 0){
				sideInfo = facesForCheck[0].id as SideInfo;
				
			
				// нужны только поверхности для стола
				if (sideInfo == null) {
					log("Side info null: " + faces[i].id);
					facesForCheck.splice(0, 1);
					continue;
				}
				
				if (sideInfo.type != SideInfo.TABLETOP_FACE) {
					facesForCheck.splice(0, 1);
					continue;
				}
				
				
				gr.clear();
				palette.beginFill(gr, "", sideInfo.width, sideInfo.height);
				DrawTool.drawEdge(gr, sideInfo.textureShape);
				palette.endFill(gr);
				
				var shapeHolder:Sprite = new Sprite();
				shapeHolder.addChild(shape);
				// reverse by Y axis
				//shape.scaleY = -1;
				//shape.y = sideInfo.height;//  excluding line width - not shape.height!!
				
				bm = new BitmapData(sideInfo.width, sideInfo.height, true, 0xFF0000);
				bm.draw(shapeHolder, null, null, null, null, true);
				
				// setup material for faces whith this sideInfo
				var textureMat:TextureMaterial = new TextureMaterial(bm, false, true, 0, 1);
				facesForCheck[0].material = textureMat;
				facesForCheck.splice(0, 1);
				
				var faceForExclude:Vector.<Face> = new Vector.<Face>();
				count = facesForCheck.length;
				for (var k:int = 0 ; k < count; k++) {
					if (facesForCheck[k].id == sideInfo) {
						facesForCheck[k].material = textureMat;
						faceForExclude.push(facesForCheck[k]);
					}
				}
				
				var index:int;
				for (k = 0; k < faceForExclude.length; k++ ) {
					index = facesForCheck.indexOf(faceForExclude[k]);
					if (index < 0) {
						
						continue;
					}
					facesForCheck.splice(index, 1);
				}
			}
		}
		
		public function setCorpusPalette(palette:IPalette):void
		{
			if (palette == null)
			{
				return;
			}
			var shape:Shape = new Shape();
			var gr:Graphics = shape.graphics;
			var bm:BitmapData;
			var sideInfo:SideInfo;
			
			var count:int = faces.length;
			var facesForCheck:Vector.<Face> = new Vector.<Face>();
			for (var i:int = 0; i < count; i++) {
				facesForCheck.push(faces[i]);
			}
			
			// обход всех поверхностей
			while (facesForCheck.length > 0) {
				
				sideInfo = facesForCheck[0].id as SideInfo;
				
				// нужны только поверхности для корпуса
				if (sideInfo == null) {
					log("corpus palette. Side info null: " + faces[i].id);
					facesForCheck.splice(0, 1);
					continue;
				}
				
				if (sideInfo.type != SideInfo.CORPUS_FACE) {
					facesForCheck.splice(0, 1);
					continue;
				}
				
				
				
				gr.clear();
				palette.beginFill(gr, "", sideInfo.width, sideInfo.height);
				DrawTool.drawEdge(gr, sideInfo.textureShape);
				palette.endFill(gr);
				
				var shapeHolder:Sprite = new Sprite();
				shapeHolder.addChild(shape);
				// reverse by Y axis
				//shape.scaleY = -1;
				//shape.y = sideInfo.height;//  excluding line width - not shape.height!!
				/*if(furnName == "decor"){
					log("w: " + sideInfo.width + " h: " + sideInfo.height);
				
					continue;
				}*/
				bm = new BitmapData(sideInfo.width, sideInfo.height, true, 0xFF0000);
				bm.draw(shapeHolder, null, null, null, null, true);
				
				
				// setup material for faces whith this sideInfo
				var textureMat:TextureMaterial = new TextureMaterial(bm, false, true, 0, 1);
				facesForCheck[0].material = textureMat;
				facesForCheck.splice(0, 1);
				
				var faceForExclude:Vector.<Face> = new Vector.<Face>();
				count = facesForCheck.length;
				
				for (var k:int = 0 ; k < count; k++) {
					if (facesForCheck[k].id == sideInfo) {
						facesForCheck[k].material = textureMat;
						faceForExclude.push(facesForCheck[k]);
					}
				}
				
				var index:int;
				for (k = 0; k < faceForExclude.length; k++ ) {
					index = facesForCheck.indexOf(faceForExclude[k]);
					if (index < 0) {
						
						continue;
					}
					facesForCheck.splice(index, 1);
				}
			}
		}
		
		
		
		public function getSizesTexture():Vector.<String>
		{
			return _model.getSizesNeeded();
		}
		
		
	}

}